from uiTool import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from markItem import MarkItem
import pandas as pd
from pandas.api.types import is_numeric_dtype
import sys
import os
import os.path as op
import time
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['SimHei']  # 步骤一（替换sans-serif字体）
plt.rcParams['axes.unicode_minus'] = False
#所有的累计量都去掉
#功率因数和润滑油压力为常量，无法计算相关性，去掉
#电机冷却水压力与其它的各项相关性都较低，去掉 
#瞬时流量和出口压力刚好是对称的，只取一个就行，！！！不能去掉
#三相交流电有A、B、C三个相，这里三个相电压是重合的，三个相电流也是重合的，都取A相的使用，！！！不能去掉
#定子温度、定子前部温度、定子中部温度、定子后部温度基本上是重合的，取一个定子温度，！！！不能去掉
#有功功率和视在功率成倍数关系，只取有功功率。有功功率、无功功率、视在功率这三个物理量绘成封闭的三角形来理解，即：有功功率与视在功率的夹角的余弦值为功率因数！！！不能去掉
#要预警该停机还是该干嘛，结果要指出大概发生了什么问题
#感觉还是简单的分类设阈值，看走势。
#感觉模拟数据不靠谱
#非常重要，注意数据缺失了，要处理
#要有个能开窗口的软件
#用模拟数据验证，造数据
#轴承异常，概率是润滑油异常
#界面设计前先写下文档交流下
PRESET_FIELDS = ['B项电流','B项电压','C项电流', 'C项电压','瞬时流量','功率因数','润滑油压力','电机冷却水压力', '定子中部温度', '定子后部温度','定子前部温度','视在功率','出口压力',
				'A项电流','A相电压','定子温度', '有功功率',  '前轴承温度', '无功功率','进口压力'
				'泵后轴温度', '泵前轴温度',  '后轴承温度', '周波', '瞬时流量']
PRESET_FIELDS_SET = set(PRESET_FIELDS)


def getCSVFileList(dirPath):
	ls = []
	if not op.exists(dirPath):
		return ls
	for file in os.listdir(dirPath):
		if file[-4:] == '.csv':
			ls.append(file)
	return ls


class Tool(QWidget, Ui_Form):
	TIME_FORMATS = 'yyyy-MM-dd hh:mm:ss'
	
	def __init__(self, parent=None):
		super(Tool, self).__init__(parent)
		self.setupUi(self)
		self.connectSignals()
		self.le_period.setValidator(QIntValidator())
		self.data = None
		self.timeField = self.le_timeField.text()
		self.markItemMap = {}
		self.drawData = None
	
	def connectSignals(self):
		self.le_dataDir.textChanged.connect(self.onDataDirChanged)
		self.cb_csvFile.currentTextChanged.connect(self.onDataFileChanged)
		self.le_timeField.textChanged.connect(self.onTimeFieldChanged)
		self.pb_draw.clicked.connect(self.onDraw)
		self.checkBox_selectAll.stateChanged.connect(self.onSelectAllStateChanged)
		self.cb_allTime.stateChanged.connect(lambda checked: self.gb_subTime.setChecked(not checked))
		self.gb_subTime.toggled.connect(lambda checked: self.cb_allTime.setChecked(not checked))
		self.pb_addMark.clicked.connect(self.onAddMarkClicked)
		self.pb_writeOut.clicked.connect(self.onWriteOutClicked)
	
	def dts(self, obj):
		if type(obj) == str:
			return QDateTime.fromString(obj, Tool.TIME_FORMATS)
		elif type(obj) == QDateTime:
			return obj.toString(Tool.TIME_FORMATS)
		else:
			return None
	
	def onDataDirChanged(self, text):
		ls = getCSVFileList(text)
		if ls == []:
			return
		self.dataDir = text
		self.cb_csvFile.clear()
		self.cb_csvFile.addItems(ls)
	
	def onDataFileChanged(self, text):
		if text == "":
			return
		path = self.dataDir + '/' + text
		self.data = pd.read_csv(path, index_col=0, encoding="utf-8")
		self.data = self.data.dropna(axis=1, how='all')
		self.updateFields()
		self.onTimeFieldChanged()
	
	def onSelectAllStateChanged(self, checked):
		if checked:
			self.lw_fields.selectAll()
		else:
			self.lw_fields.clearSelection()
	
	def onTimeFieldChanged(self):
		field = self.le_timeField.text()
		if self.data is None or field not in list(self.data.columns):
			return
		end, begin = self.data[field].iloc[-1], self.data[field].iloc[0]
		try:
			self.dts(begin)
		except:
			print("时间字段不是正确的时间格式")
			return
		self.timeField = self.le_timeField.text()
		begin = self.dts(begin)
		end = self.dts(end)
		self.dte.setMinimumDateTime(begin)
		self.dte.setMaximumDateTime(end)
		self.dte.setDateTime(begin)
	
	def onAddMarkClicked(self):
		no = self.lw_mark.count() + 1
		item = QListWidgetItem(str(no), self.lw_mark)
		markItem = MarkItem()
		self.markItemMap[markItem] = item
		item.setSizeHint(markItem.sizeHint())
		self.lw_mark.addItem(item)
		self.lw_mark.setItemWidget(item, markItem)
		markItem.deleteClicked.connect(self.onDeleteMarkItem)
	
	def onDeleteMarkItem(self):
		markItem = self.sender()
		self.lw_mark.takeItem(self.lw_mark.row(self.markItemMap[markItem]))
		self.markItemMap.pop(markItem)
	
	def updateFields(self):
		fields = list(self.data.columns)
		if self.le_timeField.text() in fields:
			fields.remove(self.le_timeField.text())
		rm = []
		for field in fields:
			if not is_numeric_dtype(self.data[field]) or \
					"累积" in field or \
					"累计" in field:
				rm.append(field)
		fields = set(fields) - set(rm)
		if PRESET_FIELDS_SET.issubset(fields):
			fields = PRESET_FIELDS_SET
		self.setupAvailableFields(list(fields))
	
	def setupAvailableFields(self, fields):
		self.lw_fields.clear()
		for field in fields:
			self.lw_fields.addItem(QListWidgetItem(field, self.lw_fields))
	
	def onWriteOutClicked(self):
		path = self.le_outputPath.text()
		if path == "" or self.drawData is None:
			return
		self.drawData.to_csv(path + '.csv', index=0)
	
	def formatterFuncDay(self, x, pos):
		return "%.1f" % (x / 1440.0)
	
	def formatterFuncHour(self, x, pos):
		return "%.1f" % (x / 60.0)
	
	def onDraw(self):
		if self.data is None:
			return
		selectedFields = list(map(lambda item: item.text(), self.lw_fields.selectedItems()))
		if self.gb_subTime.isChecked():
			T = int(self.le_period.text())
			currentDateTime = self.dte.dateTime()
			beginTime = self.dts(currentDateTime.addSecs(-60 * T))
			endTime = self.dts(currentDateTime.addSecs(60 * T))
			subData = self.data[(self.data[self.timeField] > beginTime) & (self.data[self.timeField] < endTime)]
		else:
			subData = self.data
			T = subData.shape[0]
		drawData = pd.DataFrame()
		drawData[self.timeField] = subData[self.timeField]
		if self.rb_zScore.isChecked():
			for field in selectedFields:
				mean = subData[field].mean()
				std = subData[field].std()
				drawData[field] = subData[field].map(lambda x: (x - mean) / std)
		elif self.rb_minMax.isChecked():
			for field in selectedFields:
				min = subData[field].min()
				max = subData[field].max()
				ran = max - min
				drawData[field] = subData[field].map(lambda x: (x - min) / ran)
		else:
			for field in selectedFields:
				drawData[field] = subData[field]
		plt.figure()
		index = [i for i in range(drawData.shape[0])]
		# selectedFields = PRESET_FIELDS
		for field in selectedFields:
			plt.plot(index, drawData[field], label=field)
		if T > 1440:
			plt.gca().xaxis.set_major_formatter(plt.FuncFormatter(self.formatterFuncDay))
			plt.xlabel("天")
		elif T > 60:
			plt.gca().xaxis.set_major_formatter(plt.FuncFormatter(self.formatterFuncHour))
			plt.xlabel("小时")
		else:
			plt.xlabel("分钟")
		plt.legend()
		minDateTime = self.dts(drawData.iloc[0][self.timeField])
		maxDateTime = self.dts(drawData.iloc[-1][self.timeField])
		for markItem in self.markItemMap.keys():
			markDateTime = markItem.getDateTime()
			if markDateTime < minDateTime or markDateTime > maxDateTime:
				markItem.setBackgroundColor(255, 191, 191)
				continue
			markItem.setBackgroundColor(166, 210, 255)
			diffMinutes = minDateTime.secsTo(markDateTime) / 60
			plt.axvline(diffMinutes)
		self.drawData = drawData
		plt.show()


app = QApplication(sys.argv)
exe = Tool()
exe.show()
sys.exit(app.exec_())
